using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Web;
using System.Web.UI;
using ChannelVN.CMS.DynamicLayout.FrontEnd.Base;
using ChannelVN.Cached.Base;
using ChannelVN.Cached.Common;
using ChannelVN.Cached.Config;
using ChannelVN.Cached;
using ChannelVN.Cached.Entity;
using System.Text.RegularExpressions;

namespace ChannelVN.Cached.Base
{
    public abstract class PageBase : DynamicPageBase
    {
        #region Abstract members
        protected abstract string CacheConfigName { get; }
        protected abstract bool IsCachedChanged(long newsId, string zoneId, long tagId);
        #endregion

        
        protected bool CachedThisPage { get; private set; }
        protected bool UseDynamicLayout { get; private set; }

        protected override void OnPreInit(EventArgs e)
        {
            try
            {
                if (CacheController.IsCachedDeleting)
                {
                    var cacheSettings = CacheSettings.GetCurrentSettings();
                    if (null != cacheSettings && cacheSettings.EnableCache)
                    {
                        var pageSetting = cacheSettings.PageSettings[CacheConfigName];

                        if (pageSetting.EnableCache && pageSetting.GroupKey != CacheSettingConstants.GroupCachedKey.NoGroup)
                        {
                            var group = CacheController.GetCacheGroup(pageSetting.GroupKey);
                            //CacheController.GetMemcachedInstance(cacheSettings.PageSettings.CacheClientName).RemoveAll(group);
                            CacheController.RemoveAllMemcachedGroup(group);
                        }
                    }
                }
                else
                {
                    var cacheSettings = CacheSettings.GetCurrentSettings();
                    if (null != cacheSettings && (cacheSettings.EnableCache || cacheSettings.EnableDynamicLayout))
                    {
                        var cacheControls = new List<ControlBase>();
                        FindControlBaseRecursive((Master ?? (Control)this), ref cacheControls);

                        var pageSetting = cacheSettings.PageSettings[CacheConfigName];

                        if (null != pageSetting)
                        {
                            CachedThisPage = pageSetting.EnableCache;
                            UseDynamicLayout = pageSetting.EnableDynamicLayout;

                            var count = cacheControls.Count;
                            for (var i = 0; i < count; i++)
                            {
                                var cacheControl = cacheControls[i];
                                var controlSetting = pageSetting.ControlSettings[cacheControl.CacheConfigName];
                                if (null == controlSetting) continue;

                                cacheControl.ControlCacheSetting = controlSetting;
                                cacheControl.ClientCacheName = pageSetting.ControlSettings.CacheClientName;

                                if (!controlSetting.EnableCache) continue;

                                //if (controlSetting.IsGroupKey && IsCachedUpdating && cacheControl.IsCachedChanged(QueryStrings.NewsID, QueryStrings.CategoryID, QueryStrings.ThreadID, QueryStrings.TagID))
                                //{
                                //    CacheController.GetMemcachedInstance(pageSetting.ControlSettings.CacheClientName).RemoveAll(CacheController.GetCacheGroup(controlSetting.IsGroupKey));
                                //}
                                var group = CacheController.GetCacheGroup(controlSetting.GroupKey);

                                var cacheKey = CacheController.GetCacheKeyName(controlSetting.CacheNameFormat, pageSetting.ControlSettings.CacheClientName);

                                var cacheData = CacheController.GetMemcachedInstance(pageSetting.ControlSettings.CacheClientName).Get(group, cacheKey) as string;
                                cacheControl.CacheData = cacheData;
                            }
                        }
                    }

                    base.OnPreInit(e);
                }
            }
            catch (Exception ex)
            {
                Lib.WriteLogToFile(ex);
                base.OnPreInit(e);
            }
        }

        protected override void OnPreLoad(EventArgs e)
        {
            if (UseDynamicLayout) OnPreLoadDynamicPage();
            base.OnPreLoad(e);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            try
            {
                var strBuilder = new StringBuilder();
                using (var strWriter = new StringWriter(strBuilder))
                {
                    var virtualUrl = (null != HttpContext.Current.Items["VirtualUrl"] ? HttpContext.Current.Items["VirtualUrl"].ToString() : "");

                    using (var htmlWriter = new RewriteFormHtmlTextWriter(strWriter, virtualUrl))
                    {
                        PageSettingItem pageSetting = null;
                        var cacheSettings = CacheSettings.GetCurrentSettings();
                        if (null != cacheSettings && null != cacheSettings.PageSettings)
                        {
                            pageSetting = cacheSettings.PageSettings[CacheConfigName];
                        }
                        if (null != pageSetting) EnableViewState = pageSetting.EnableViewState;

                        //if (pageSetting.IsGroupKey && IsCachedUpdating && IsCachedChanged(QueryStrings.NewsID, QueryStrings.CategoryID, QueryStrings.ThreadID, QueryStrings.TagID))
                        //{
                        //    CacheController.GetMemcachedInstance(cacheSettings.PageSettings.CacheClientName).RemoveAll(CacheController.GetCacheGroup(pageSetting.IsGroupKey));
                        //}

                        base.Render(htmlWriter);

                        var html = strBuilder.ToString();
                        if (AppConfigs.AllowWrite1Line)
                        {
                            html = Lib.CompressHtmlContent(html);
                        }
                        writer.Write(html);

                        #region Enable GZIP
                        var context = HttpContext.Current;
                        var request = context.Request;
                        string acceptEncoding = request.Headers["Accept-Encoding"];
                        var response = context.Response;
                        if (!string.IsNullOrEmpty(acceptEncoding))
                        {
                            acceptEncoding = acceptEncoding.ToUpperInvariant();
                            if (acceptEncoding.Contains("GZIP"))
                            {
                                response.Filter = new GZipStream(context.Response.Filter,
                                CompressionMode.Compress);
                                response.AppendHeader("Content-encoding", "gzip");
                            }
                            else if (acceptEncoding.Contains("DEFLATE"))
                            {
                                response.Filter = new DeflateStream(context.Response.Filter,
                                CompressionMode.Compress);
                                response.AppendHeader("Content-encoding", "deflate");
                            }
                        }
                        response.Cache.VaryByHeaders["Accept-Encoding"] = true; 
                        #endregion

                        if (IsPostBack ||
                            null == cacheSettings ||
                            !cacheSettings.EnableCache ||
                            null == pageSetting ||
                            !pageSetting.EnableCache ||
                            !cacheSettings.EnableHtmlCached) return;

                        if ((!CacheController.IsCachedUpdating) || (CacheController.IsCachedUpdating &&
                            IsCachedChanged(QueryStrings.CachedNewsId, QueryStrings.CachedZone, QueryStrings.CachedTagId)))
                        {
                            var group = CacheController.GetCacheGroup(pageSetting.GroupKey);
                            var cacheKey = CacheController.GetCacheKeyName(pageSetting.CacheNameFormat, cacheSettings.PageSettings.CacheClientName);
                            CacheController.GetMemcachedInstance(cacheSettings.PageSettings.CacheClientName).Add(group, cacheKey, html, pageSetting.CacheExpire);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Lib.WriteLogToFile(ex);
                base.Render(writer);
            }
        }

        private void FindControlBaseRecursive(Control currentControl, ref List<ControlBase> cacheControls)
        {
            for (var i = 0; i < currentControl.Controls.Count; i++)
            {
                var control = currentControl.Controls[i];

                if (control is ControlBase)
                {
                    cacheControls.Add(control as ControlBase);
                }

                FindControlBaseRecursive(control, ref cacheControls);
            }
        }

        private class RewriteFormHtmlTextWriter : HtmlTextWriter
        {
            private readonly string _formAction;

            public RewriteFormHtmlTextWriter(TextWriter writer, string action)
                : base(writer)
            {
                if (!string.IsNullOrEmpty(action))
                {
                    _formAction = action;
                }
            }
            public RewriteFormHtmlTextWriter(TextWriter writer)
                : base(writer)
            {
            }

            public override void RenderBeginTag(string tagName)
            {
                if (tagName.IndexOf("form") >= 0)
                {
                    base.RenderBeginTag(tagName);
                }
            }

            public override void WriteAttribute(string name, string value, bool fEncode)
            {
                if ("action" == name && !string.IsNullOrEmpty(_formAction))
                {
                    //value = _formAction;
                    value = ""; //HttpContext.Current.Request.Url.AbsolutePath;
                }
                base.WriteAttribute(name, value, fEncode);
            }
        }
    }
}
